Windows Kernel Exploitation Tutorial Part 8: Use After Free

Overview

In our previous post, we discussed about Uninitialized Heap Variable. This post will focus on another vulnerability, Use After Free. As the name might suggest, we’d be exploiting a stale pointer, that should’ve been freed, but due to a flaw, the pointer is called through a Callback function, thus executing anything that we can put into the memory there.

Again, huge thanks to @hacksysteam for the driver.


Analysis

The analysis part on this vulnerability is a multi-step breakdown of different functions used in the UseAfterFree.c file. Just reading through the file gives us 4 different functions, that seems useful to what we have to analyze here. We’d look into each of the functions one by one below:

First, we look into the AllocateUafObject() function. As the name suggests, this will allocate a Non-Paged pool chunk, fill it with ‘A’s, terminated with a NULL character.

Next we look into the FreeUaFObject() function. As we see here, if we compile our driver with the SECURE flag, the g_UseAfterFreeObject is being set to NULL, whereas in the vulnerable version, ExFreePoolWithTag is being used, which will leave a reference to a stale dangling pointer. A good explanation provided by @hacksysteam here.

UseUaFObject(). Simple function, this is just calling the callback on g_UseAfterFreeObject if a pointer exists. This is where the dangling pointer proves to be dangerous and as the name suggests, this is what we are going to exploit.

We’d using AllocateFakeObject() function to place our shellcode pointer into the non-paged pool.


Exploitation

We can start with our basic skeleton script, but here, if we look into HackSysExtremeVulnerableDriver.h file, we notice that there’re different CTL codes for ALLOCATE_UAF_OBJECT, USE_UAF_OBJECT, FREE_UAF_OBJECT and ALLOCATE_FAKE_OBJECT. So, IOCTLs for each of them needs to be calculated, and then used in our exploit as we need it according to the process. Using our old method to calculate IOCTL codes, it comes up to 0x222013, 0x222017, 0x22201B and 0x22201F respectively. We’ll try each of them just to make sure they work perfectly:

Allocating UaF Object

Freeing UaF Object

Allocating Fake Object. Notice that our fake_obj is perfectly stored into our fake object address

Use the stale UaF Object

Everything works as expected. Now before we proceed further to craft our exploit, let’s clear up the pathway on how we’d need to proceed. The overall flow of the execution should be on the lines of:

  • Groom the non-paged pool in predictable manner.
  • Allocate the UAF objects
  • Free the UAF objects.
  • Allocating the fake objects, containing our shellcode pointer.
  • Calling the stale UAF pointer with the callback function, which will ultimately execute our shellcode, residing in the pointer address.

Simple enough, we’d proceed in accordance to the steps above. First thing we’d be doing is grooming the non-paged pool. I’d be using IoCompletionReserve objects from Tarjei Mandt’s paper, as it has the perfect size of 0x60 to groom our non-paged pool, and it’s closer to the size of our UAF object. These objects can be sprayed using NtAllocateReserveObject function.

Borrowing the spraying logic from our Pool Overflow tutorial, the script looks like:

Sprayed 0x3a99 (15001) objects in Non-Paged Pool, and we can see our spray is pretty consistent

Now that our pool is sprayed, we need to create holes in it for our exploit to dig in. But, the challenge here would be to prevent coalescence, as if subsequent free chunks are found, they’d be coalesced, and our groomed pool would go into an unpredictable state. To prevent this, we’d be freeing alternate chunks in the sprayed region:

Alternate Objects are freed to avoid coalescence

Now that our pool is in predictable state, we’d call our IOCTLs in the exact order as described above. For the ALLOCATE_FAKE_OBJECT, for now, we’d be allocating the same junk as previously demonstrated:

Fake objects allocated, and our callback address is being pointed to our garbage fake object.

The spray is perfect with our sprayed value and the Pool tag.

Perfect, our fake objects are exactly where we want them to be, and our callback pointer is in our control. Now the only thing left is to insert our shellcode pointer (borrowed from previous tutorials) in place, and we should get our nt authority\system shell:

2 thoughts on “Windows Kernel Exploitation Tutorial Part 8: Use After Free

  1. Thanks Ratul, very well-written and useful posts in this Windows kernel exploitation series!

    What I noticed with this HEVD UAF vulnerability is that it does not need to be exploited by first applying grooming. Simply one AllocateUaFObject, followed by the FreeUaFObject, AllocateFakeObject and UseUaFObject will also consistently make it possible to exploit the vulnerability.

    OS used: 7601.17514.x86fre.win7sp1_rtm.101119-1850

Leave a Reply

Your email address will not be published. Required fields are marked *